# -*- coding: utf-8 -*-

# Define your item pipelines here
#
# Don't forget to add your pipeline to the ITEM_PIPELINES setting
# See: http://doc.scrapy.org/en/latest/topics/item-pipeline.html

from scrapy import log
from twisted.enterprise import adbapi

#from pyes import ES
from txes2 import ElasticSearch

from torrent_crawler import torrent_name


class TorrentNameTokenizePipeline(object):

    def process_item(self, item, spider):
        item['tags'] = torrent_name.tokenize(item['name'])
        return item


class MySQLStorePipeline(object):
    """A pipeline to store the item in a MySQL database.
    This implementation uses Twisted's asynchronous database API.
    """

    def __init__(self, dbpool):
        self.dbpool = dbpool

    @classmethod
    def from_settings(cls, settings):
        dbargs = dict(
            host=settings['MYSQL_HOST'],
            db=settings['MYSQL_DBNAME'],
            user=settings['MYSQL_USER'],
            passwd=settings['MYSQL_PASSWD'],
            charset='utf8',
            use_unicode=True,
        )
        dbpool = adbapi.ConnectionPool('MySQLdb', **dbargs)
        return cls(dbpool)

    def process_item(self, item, spider):
        # run db query in the thread pool
        d = self.dbpool.runInteraction(self._do_upsert, item, spider)
        d.addErrback(self._handle_error, item, spider)
        # at the end return the item in case of success or failure
        d.addBoth(lambda _: item)
        # return the deferred instead the item. This makes the engine to
        # process next item (according to CONCURRENT_ITEMS setting) after this
        # operation (deferred) has finished.
        return d

    def _do_upsert(self, conn, item, spider):
        """Perform an insert or update."""
        title = item['title']
        info_hash = item['info_hash']
        magnet = item['magnet_link']

        conn.execute("""SELECT EXISTS(
                     SELECT 1 FROM torrents WHERE info_hash = %s
                     )""", (info_hash, ))
        ret = conn.fetchone()[0]

        if not ret:
            conn.execute("""
                         INSERT INTO torrents (title, info_hash, link)
                         VALUES (%s, %s, %s)
                         """, (title, info_hash, magnet))
            spider.log("Item stored in db: %s %r" % (info_hash, item))
        else:
            conn.execute("""
                         UPDATE torrents
                         SET
                         title = %s,
                         link = %s
                         WHERE
                         info_hash = %s
                         """, (title, magnet, info_hash))

    def _handle_error(self, failure, item, spider):
        """Handle occurred on db interaction."""
        # do nothing, just log
        log.err(item['info_hash'] + failure)


class ElasticSearchPipeline(object):
    settings = None
    es = None

    @classmethod
    def from_crawler(cls, crawler):
        ext = cls()
        ext.settings = crawler.settings

        basic_auth = {}

        if (ext.settings['ELASTICSEARCH_USERNAME']):
            basic_auth['username'] = ext.settings['ELASTICSEARCH_USERNAME']

        if (ext.settings['ELASTICSEARCH_PASSWORD']):
            basic_auth['password'] = ext.settings['ELASTICSEARCH_PASSWORD']

        server = ext.settings['ELASTICSEARCH_SERVER']
        port = ext.settings['ELASTICSEARCH_PORT']
        if port:
            uri = "%s:%d" % (server, port)
        else:
            uri = "%s" % (server)

        ext.es = ElasticSearch(uri)
        return ext

    def index_item(self, item):
        info_hash = item['info_hash']
        op_type = 'index'
        return self.es.index(dict(item),
                             self.settings.get('ELASTICSEARCH_INDEX'),
                             self.settings.get('ELASTICSEARCH_TYPE'),
                             id=info_hash,
                             op_type=op_type)

    def process_item(self, item, spider):
        d = self.index_item(item)
        d.addBoth(lambda _: item)
        d.addErrback(self._handle_error, item, spider)
        return d

    def _handle_error(self, failure, item, spider):
        # do nothing, just log
        log.err(failure)
